#include "singleton_container.h"

#include <mutex>

std::shared_ptr<SingletonContainer> SingletonContainer::GetInstance()
{
    if (instance == nullptr) {
        static std::mutex mutex;
        std::lock_guard<std::mutex> lock(mutex);
        if (instance == nullptr) {
            instance = std::make_shared<SingletonContainer>();
        }
    }
    return instance;
}

SingletonContainer::~SingletonContainer()
{
    while (moduleMap.empty() == false) {
        for (auto it = moduleMap.begin(); it != moduleMap.end(); it++) {
            if (it->second.refCount == 0) {
                printf("removing: %d\n", it->first);
                if (dependencySetMap.find(it->first) != dependencySetMap.end()) {
                    for (auto mid : dependencySetMap[it->first]) {
                        moduleMap[mid].refCount--;
                    }
                    dependencySetMap.erase(it->first);
                }
                moduleMap.erase(it);
                break;
            }
        }
    }
    printf("%s\n", __func__);
}

void SingletonContainer::AddSingleton(const std::string &name, const std::any &instance)
{
    if (stringMap.find(name) == stringMap.end()) {
        printf("AddSingleton: %s\n", instance.type().name());
        static int32_t nextId = 0;
        moduleMap[nextId].value = instance;
        moduleMap[nextId].refCount = 0;
        stringMap[name] = nextId++;
    }
}

void SingletonContainer::SetSingleton(const std::string &name, const std::any &instance)
{
    if (stringMap.find(name) == stringMap.end()) {
        AddSingleton(name, instance);
    } else {
        moduleMap[stringMap[name]].value = instance;
    }
}

const std::any& SingletonContainer::GetSingleton(const std::string &name)
{
    if (stringMap.find(name) == stringMap.end()) {
        static std::any voidAny;
        return voidAny;
    }
    printf("GetSingleton: %s\n", moduleMap[stringMap[name]].value.type().name());
    return moduleMap[stringMap[name]].value;
}

const std::any& SingletonContainer::DependOn(const std::string &instance, const std::string &name)
{
    auto &instanceDependencySet = dependencySetMap[stringMap[instance]];
    if (instanceDependencySet.find(stringMap[name]) == instanceDependencySet.end()) {
        printf("%s DependOn %s", instance.c_str(), name.c_str());
        instanceDependencySet.insert(stringMap[name]);
        moduleMap[stringMap[name]].refCount++;
    }
    return GetSingleton(name);
}
